/**
 *  Copyright 2002-2009 the original author or authors.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package foo.bar.wiki.services;

import foo.bar.wiki.domain.Identifiable;
import foo.bar.wiki.web.action.ExtendedPaginatedList;
import foo.bar.wiki.domain.Identifiable;
import foo.bar.wiki.domain.User;
import foo.bar.wiki.web.action.ExtendedPaginatedList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.jpa.JpaTemplate;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.TransactionStatus;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.EntityTransaction;
import java.util.List;

/**
 * An abstract service class that provides commons services required by service subclass
 *
 * @see {@link #getJpaTemplate()}
 * @see {@link #getFirstResult(javax.persistence.Query)}
 * @see {@link #saveOrUpdate(javax.persistence.EntityManager, foo.bar.wiki.domain.Identifiable)}
 * @see {@link #prepareQueryWithPaginatedListInfo(foo.bar.wiki.web.action.ExtendedPaginatedList, javax.persistence.Query)}
 * @see {@link #populatePagniatedList(foo.bar.wiki.web.action.ExtendedPaginatedList, java.util.List, long)}
 *
 * @author tmjee
 * @version $Date$ $Id$
 */
public class AbstractJpaAwareService {

    private static final Log LOG = LogFactory.getLog(AbstractJpaAwareService.class);

    AbstractJpaAwareService() {}

    private JpaTemplate template;

    public AbstractJpaAwareService(JpaTemplate template) {
        this.template = template;
    }


    private TransactionTemplate transactionTemplate;
    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }


    /**
     * Returns a {@link JpaTemplate}.
     *
     * @return {@link JpaTemplate}
     */
    protected JpaTemplate getJpaTemplate() {
        return template;
    }


    /**
     * Return's the first result of {@link Query}, doesn't throw exception if {@link Query}
     * even if the {@link Query} contains many entries in it unlike
     * {@link javax.persistence.Query#getSingleResult()} does.
     *
     * @param query
     * @return Object
     */
    protected Object getFirstResult(Query query) {
        if (query != null) {
            List results = query.getResultList();
            if (results != null && results.size() > 0) {
                Object o = results.get(0);
                return o;
            }
        }
        return null;
    }


    /**
     * Perform either
     * <ul>
     *  <li>entityManager.persist(identifiable);</li>
     *  <li>entityManager.merge(identifiable);
     * </ul>
     * depending on the state of identifiable.
     * <p/>
     * If identifiable's {@link foo.bar.wiki.domain.Identifiable#getId()} is null, it performs
     * an entityManager.persist(identifiable); else it performs entityManager.merge(identifiable)
     *
     * @param entityManager
     * @param identifiable
     * @return {@link Identifiable}
     */
    protected Identifiable saveOrUpdate(final EntityManager entityManager, final Identifiable identifiable) {
        if (identifiable.getId() == null) {
            entityManager.persist(identifiable);
            entityManager.flush();
            return identifiable;
        } else {
            return entityManager.merge(identifiable);
        }
    }

    /**
     * prepare the {@link Query} object by populating the pagination info eg. :-
     * <ul>
     *   <li> page number ({@link foo.bar.wiki.web.action.ExtendedPaginatedList#getPageNumber()}) </li>
     *   <li> objects per page ({@link foo.bar.wiki.web.action.ExtendedPaginatedList#getObjectsPerPage()}) </li>
     * </ul>
     * into the {@link Query) object and return it back
     *
     * @param paginatedList
     * @param query
     * @return {@link javax.persistence.Query}
     */
    protected Query prepareQueryWithPaginatedListInfo(ExtendedPaginatedList paginatedList, Query query) {
        int firstResult = (paginatedList.getPageNumber() > 0 ? paginatedList.getPageNumber() - 1 : 0);
        int maxResult = (paginatedList.getObjectsPerPage() > 0 ? paginatedList.getObjectsPerPage() : 10);
        query.setFirstResult((firstResult * maxResult));
        query.setMaxResults(maxResult);
        return query;
    }

    /**
     * populate pagination result eg :-
     * <ul>
     *  <li>The result list for this particular page</li>
     *  <li>The total entries</li>
     * </ul>
     * into the {@link foo.bar.wiki.web.action.ExtendedPaginatedList} and return it back.
     *
     * @param paginatedList
     * @param list
     * @param size
     * @return {@link foo.bar.wiki.web.action.ExtendedPaginatedList}
     */
    protected ExtendedPaginatedList populatePagniatedList(ExtendedPaginatedList paginatedList, List list, long size) {
        paginatedList.setList(list);
        paginatedList.setFullListSize((int)size);
        return paginatedList;
    }
}
